using System;
using System.Collections.Generic;
using Clandestine.Base;

namespace Clandestine.Input
{ 
	public class InputManager<T> where T : struct, IConvertible
	{
		public delegate void ButtonEvent(T role);
	
		protected Dictionary<T, Button> buttonMapping = new Dictionary<T, Button>();
		public JoystickDevice[] Joysticks;
		public KeyboardDevice Keyboard = new KeyboardDevice();
		public event ButtonEvent ButtonPressed;
		public event ButtonEvent ButtonReleased;
		
		public InputManager ()
		{
			// Check that T is Enum!
			if (!typeof(T).IsEnum)
				throw new Exception("InputManager can only be used with enums!");
			
			// Setup joysticks
			// Assume OSX for now [TODO]
			Joysticks = OSX.OSXDeviceLoader.LoadJoysticks().ToArray();
			
			// Events!
			foreach (JoystickDevice joy in Joysticks)
			{
				joy.ButtonPressed += onButtonPressed;
				joy.ButtonReleased += onButtonReleased;
			}
			Keyboard.ButtonPressed += onButtonPressed;
			Keyboard.ButtonReleased += onButtonReleased;
		}
		
		public void ProcessEvents()
		{	
			// Joysticks
			foreach (JoystickDevice joy in Joysticks)
				joy.ProcessEvents();
		}
		
		public void Map(T role, Button button)
		{
			lock (this.buttonMapping)
			{
				// Existing mapping for this role? If so, remove
				if (this.buttonMapping.ContainsKey(role))
					this.buttonMapping.Remove(role);
					
				// Check if we've another role that uses this button
				if (this.buttonMapping.ContainsValue(button))
					Log.w("Multiple roles assigned to the same button. Allowing.");
					
				this.buttonMapping.Add(role, button);
			}
		}
		
		public void Unmap(T role)
		{
			lock (this.buttonMapping)
				if (this.buttonMapping.ContainsKey(role))
					this.buttonMapping.Remove(role);
		}
		
		public bool IsButtonPressed(T buttonRole)
		{
			// Mapped?
			if (!this.buttonMapping.ContainsKey(buttonRole))
			{
				Log.w("IsButtonPressed was called with an unmapped button role, returning false.");
				return false;
			}
			
			Button button = this.buttonMapping[buttonRole];
			return button.Device.IsButtonPressed(button.Number);
		}
		
		protected void onButtonPressed (IButtonDevice device, int n)
		{
			raiseButtonEvent(ButtonPressed, device, n);
		}

		protected void onButtonReleased (IButtonDevice device, int n)
		{
			raiseButtonEvent(ButtonReleased, device, n);	
		}
		
		protected void raiseButtonEvent(ButtonEvent e, IButtonDevice device, int n)
		{
			if (e == null)
				return;
		
			// Loop through the mappings and get all the ones that match our device, and number
			List<T> matches = new List<T>();
			foreach (KeyValuePair<T,Button> kvp in this.buttonMapping)
				if ((kvp.Value.Device == device) && (kvp.Value.Number == n))
					matches.Add(kvp.Key);
					
			// Raise events
			foreach (T match in matches)
				e(match);
		}
	}
}

